//==============================================================================
// Author: Fabrizio Vita
// Date: 2011-01-13
// Copyright: (c)2010-2011 ItacaSoft di Vita Fabrizio. ALL RIGHTS RESERVED.
//===============================================================================
unit Circlog;

interface

uses Windows, SysUtils, Classes;

const
  HEADERSIZE = 15;
  LINE_SIZE = 1024;

type
  TLogLevel = (LogLogorrheic, LogVerbose, LogNormal, LogWarnings, LogErrors);

  ///This class manages a simple circular log on file
  TCircLog = class(TObject)
  private
    LogFileName: string;  { Nome del file di LOG }
    MaxLogSize: integer;
    iCurrPosition: integer;
    fLogLevel: TLogLevel;
    procedure CreateFile;
    procedure InternalWriteLn (requestNum: integer; sLine: ansistring; writeTime: bool = true);
  public
    //constructor of the class
    constructor Create(sFileName: string; level: TLogLevel);
    //destructor of the class
    destructor Destroy; override;
    procedure WriteErrorStrings (requestNum: integer; Lines: TStrings);
    procedure WriteLn (requestNum: integer; sLine: ansistring; level: TLogLevel = LogNormal; writeTime: bool = true); overload;
    procedure WriteLn (requestNum: integer; sLine: string; level: TLogLevel = LogNormal; writeTime: bool = true); overload;
    procedure WriteWarningLn (requestNum: integer; sLine: ansistring; writeTime: bool = true); overload;
    procedure WriteWarningLn (requestNum: integer; sLine: string; writeTime: bool = true); overload;
    procedure WriteErrorLn (requestNum: integer; sLine: ansistring; writeTime: bool = true); overload;
    procedure WriteErrorLn (requestNum: integer; sLine: string; writeTime: bool = true); overload;
    property LogLevel: TLogLevel read fLogLevel;
  end;

var
   globallog : TCirclog;

implementation

uses ConversionUtils, SyncObjs, Inifiles, Globals;

const
   LOG_VERBOSE_SIZE = 30000000;
   LOG_SIZE = 3000000;

{ ********************************* TCIRCLOG ********************************* }
procedure TCircLog.CreateFile;
var
  Stream: TFileStream;
  szInFileBuff: array [0..LINE_SIZE] of ansichar;
  F: file of Byte;
begin
   AssignFile(F, LogFileName);
   Rewrite(F);
   CloseFile(F);

   try
      if useLogCriticalSections then
         LogCriticalSection.Acquire;

      Stream := TFileStream.Create (LogFileName, fmOpenReadWrite or fmShareDenyWrite);
      iCurrPosition := 0;
      StrPCopy(szInFileBuff, Format('%13d', [iCurrPosition]) + #13#10);
      Stream.WriteBuffer (szInFileBuff, HEADERSIZE);
   finally
      Stream.Free;
      if useLogCriticalSections then
         LogCriticalSection.Release;
   end;
end;

constructor TCircLog.Create(sFileName: string; level: TLogLevel);
var
  Stream: TFileStream;
  szInFileBuff: array [0..LINE_SIZE] of ansichar;
begin
   inherited Create;

   Stream := nil;

   fLogLevel := level;
   LogFileName := sFileName;

   if level = LogVerbose then
   begin
      MaxLogSize := LOG_VERBOSE_SIZE;
   end else
   begin
      MaxLogSize := LOG_SIZE;
   end;

   if MaxLogSize > 100 then
   begin
      if FileExists(LogFileName) then
      begin
         try
            if useLogCriticalSections then
               LogCriticalSection.Acquire;

            Stream := TFileStream.Create (LogFileName, fmOpenReadWrite or fmShareDenyWrite);
            Stream.ReadBuffer (szInFileBuff, LongInt(HEADERSIZE - 2));
            szInfileBuff[HEADERSIZE - 2] := chr(0); // aggiunge terminazione
            iCurrPosition := StrToInt(ansistring(szInFileBuff));
         finally
            if assigned(Stream) then
               Stream.free;
            if useLogCriticalSections then
               LogCriticalSection.Release;
         end;
      end else
      begin
         CreateFile;
      end;
  end else //if not MaxLogSize > 100
    raise Exception.Create('I need at least 100 byte of MaxLogSize');
end;

destructor TCircLog.Destroy;
begin
  inherited Destroy;
end;

procedure TCircLog.WriteErrorStrings (requestNum: integer; Lines: TStrings);
var
   i: integer;
begin
   if Lines.Count = 0 then exit;
   WriteErrorLn(requestNum, lines[0]);

   for i := 1 to lines.Count - 1 do
   begin
      WriteErrorLn(requestNum, '---' + lines[i], false);
   end;
end;

procedure TCircLog.InternalWriteLn (requestNum: integer; sLine: ansistring; writeTime: bool = true);
var
  Stream: TFileStream;
  szOutBuff: array [0..LINE_SIZE] of ansichar;
  LengthBuff, l: integer;
  strp: ansistring;
begin
   Stream := nil;

   if requestNum > 0 then
   begin
      strp := intToStr( requestNum );
      strp := '000' + strp;
      l := length(strp);
      strp := copy(strp, l - 2, 3);
      sline := 'REQ' + strp + ' ' + sline;
   end;

   if writeTime then
      sLine := FormatDateTime('dd/mm/yy hh:mm:ss.zzz', Now) + '  ' + sLine
   else
      sLine := '                   ' + sLine;

   StrPCopy(szOutBuff, copy(sLine, 0, LINE_SIZE-1));
   StrCat(szOutBuff, #13#10);
   LengthBuff := StrLen(szOutBuff);

{$IFDEF CONSOLE}
   System.Writeln(sLine);
{$ENDIF}

   try
      if useLogCriticalSections then
         LogCriticalSection.Acquire;

      try
         Stream := TFileStream.Create (LogFileName, fmOpenReadWrite or fmShareDenyWrite);
      except
         on e: exception do
         begin
            if not FileExists(LogFileName) then
            begin
               CreateFile;
               Stream := TFileStream.Create (LogFileName, fmOpenReadWrite or fmShareDenyWrite);
            end;
         end;
      end;

      { posiziona il puntatore e scrive il comando }
      Stream.Seek (iCurrPosition + HEADERSIZE, soFromBeginning);
      Stream.WriteBuffer(szOutBuff, LengthBuff);

      { aggiorno iCurrPosition. Se iCurrPosition eccede MaxLogSize, allora lo riporto a 0 }
      iCurrPosition := iCurrPosition + LengthBuff;
      if iCurrPosition > MaxLogSize then iCurrPosition := 0;

      { porto il puntatore sulla positione + lo spazio per l'header }
      Stream.Seek (iCurrPosition + HEADERSIZE, soFromBeginning);
      StrPCopy(szOutBuff, '>>>>>>>>>>>>>>> BEGIN OF CIRCULAR FILE' + #13#10);
      Stream.WriteBuffer(szOutBuff, 40);
      { scrive header del logfile }
      StrPCopy(szOutBuff, Format('%13d', [iCurrPosition]) + #13#10);
      Stream.Seek (0, soFromBeginning);
      Stream.WriteBuffer (szOutBuff, HEADERSIZE);
   finally
      if assigned(Stream) then
         Stream.Free;
      if useLogCriticalSections then
         LogCriticalSection.Release;
   end;
end;

procedure TCircLog.WriteErrorLn (requestNum: integer; sLine: ansistring; writeTime: bool = true);
begin
   if (flogLevel <= LogErrors) then
     InternalWriteLn(requestNum, '[ERR] ' + sLine, writetime);
end;

procedure TCircLog.WriteErrorLn (requestNum: integer; sLine: string; writeTime: bool = true);
begin
   if (flogLevel <= LogErrors) then
     InternalWriteLn(requestNum, '[ERR] ' + sLine, writetime);
end;


procedure TCircLog.WriteWarningLn (requestNum: integer; sLine: ansistring; writeTime: bool = true);
begin
   if (flogLevel <= LogWarnings) then
     InternalWriteLn(requestNum, '[WRN] ' + sLine, writetime);
end;

procedure TCircLog.WriteWarningLn (requestNum: integer; sLine: string; writeTime: bool = true);
begin
   if (flogLevel <= LogWarnings) then
     InternalWriteLn(requestNum, '[WRN] ' + sLine, writetime);
end;

procedure TCircLog.WriteLn (requestNum: integer; sLine: ansistring; level: TLogLevel = LogNormal; writeTime: bool = true);
begin
   if (flogLevel <= level) then
     InternalWriteLn(requestNum, sLine, writetime);
end;

procedure TCircLog.WriteLn (requestNum: integer; sLine: string; level: TLogLevel = LogNormal; writeTime: bool = true);
begin
   if (flogLevel <= level) then
     InternalWriteLn(requestNum, sLine, writetime);
end;


initialization

finalization

end.
